// +FHDR------------------------------------------------------------
//                 Copyright (c) 2024 NOVAUTO.
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : sync_hand_fifo_reg.v
// Author        : ICer
// Created On    : 2024-01-04 11:18
// Last Modified : 2024-01-06 17:33 by ICer
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------


module sync_hand_fifo_reg #(
    //parameter
    parameter WIDTH = 8,
    parameter DEPTH = 8
)( /*AUTOARG*/
   // Outputs
   data_in_ready, data_out_valid, data_out,
   // Inputs
   clk, rst_n, data_in_valid, data_in, data_out_ready
   );

// ----------------------------------------------------------------
// Interface declare
// ----------------------------------------------------------------
input             clk;
input             rst_n;

input             data_in_valid;
input [WIDTH -1:0]data_in;
output            data_in_ready;

output            data_out_valid;
output[WIDTH -1:0]data_out;
input             data_out_ready;

// ----------------------------------------------------------------
// Wire declare
// ----------------------------------------------------------------
localparam ADDR_W    = $clog2(DEPTH);
localparam ADDR_W_EX = ADDR_W + 1;

wire data_in_hand_en = (data_in_valid && data_in_ready);
wire data_out_hand_en= (data_out_valid && data_out_ready);

// ----------------------------------------------------------------
// AUTO declare
// ----------------------------------------------------------------
/*AUTOOUTPUT*/
/*AUTOINPUT*/
/*AUTOWIRE*/

// ----------------------------------------------------------------
// write cnt, use ADDR_W_EX as WD, high bit is loop-flag
// ----------------------------------------------------------------
reg [ADDR_W_EX -1:0]waddr;
wire                wenc;
wire[ADDR_W_EX -1:0]waddr_d;
wire                waddr_d_h;
wire[ADDR_W    -1:0]waddr_d_l;

assign wenc      = data_in_hand_en;
assign waddr_d_h = (waddr[ADDR_W -1:0] == DEPTH-1) ? ~waddr[ADDR_W] : waddr[ADDR_W];
assign waddr_d_l = (waddr[ADDR_W -1:0] == DEPTH-1) ? {ADDR_W{1'b0}} : waddr[ADDR_W -1:0] + 1'b1;
assign waddr_d   = {waddr_d_h, waddr_d_l};

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    waddr <= {ADDR_W_EX{1'b0}};
  end 
  else if(wenc)begin
    waddr <= waddr_d;
  end
end

// ----------------------------------------------------------------
// read cnt, use ADDR_W_EX as WD, high bit is loop-flag
// ----------------------------------------------------------------
reg [ADDR_W_EX -1:0]raddr;
wire                renc;
wire[ADDR_W_EX -1:0]raddr_d;
wire                raddr_d_h;
wire[ADDR_W    -1:0]raddr_d_l;

assign renc      = data_out_hand_en;
assign raddr_d_h = (raddr[ADDR_W -1:0] == DEPTH-1) ? ~raddr[ADDR_W] : raddr[ADDR_W];
assign raddr_d_l = (raddr[ADDR_W -1:0] == DEPTH-1) ? {ADDR_W{1'b0}} : raddr[ADDR_W -1:0] + 1'b1;
assign raddr_d   = {raddr_d_h, raddr_d_l};

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    raddr <= {ADDR_W_EX{1'b0}};
  end 
  else if(renc)begin
    raddr <= raddr_d;
  end
end

//==================================================================
//fifo cnt cal
//==================================================================
wire [ADDR_W_EX -1:0]fifo_cnt = (waddr_d[ADDR_W] == raddr_d[ADDR_W]) ? (waddr_d[ADDR_W-1:0] - raddr_d[ADDR_W-1:0]):
                                                                       (waddr_d[ADDR_W-1:0] + DEPTH - raddr_d[ADDR_W-1:0]);

//==================================================================
//data reg
//==================================================================
reg [WIDTH -1:0]data[DEPTH -1:0];
always @(posedge clk)begin
  if(wenc) data[waddr[ADDR_W-1:0]] <= data_in;
end
assign data_out = data[raddr[ADDR_W-1:0]];

//==================================================================
//out logic
//==================================================================
assign data_in_ready  = (fifo_cnt < DEPTH);
assign data_out_valid = (fifo_cnt > {ADDR_W_EX{1'b0}});

endmodule
// Local Variables:
// verilog-auto-inst-param-value:t
// verilog-library-directories:(".")
// verilog-library-extensions:(".v")
// End:

